#include <stdlib.h>
#include "defines.h"
#include "global.h"
#include "assert.h"
#include "common.h"

void get_mprrFlag(bool block_available_up, bool block_available_left)
{
	// 2 DC
	if (!block_available_up && !block_available_left) {
		img->mprr_flag[DC_PRED][0] = 0;
	}

	if (block_available_up && !block_available_left) {
		img->mprr_flag[DC_PRED][0] = 0;
	}

	if (!block_available_up && block_available_left) {
		img->mprr_flag[DC_PRED][0] = 0;
	}


	if (block_available_up && block_available_left) {
		img->mprr_flag[DC_PRED][0] = 0;
	}

	// 0 vertical
	if (block_available_up) {
		img->mprr_flag[VERT_PRED][0] = 0;
	}

	// 1 horizontal
	if (block_available_left) {
		img->mprr_flag[HOR_PRED][0] = 0;
	}

	// 4 down-right
	if (block_available_left&&block_available_up) {
		img->mprr_flag[DOWN_RIGHT_PRED][0] = 0;
	}

	// 3 down-left
	if (block_available_left&&block_available_up) {
		img->mprr_flag[DOWN_LEFT_PRED][0] = 0;
	}
}

// intrapred mode operation

void xPredIntraLumaDC(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight, bool bAboveAvail, bool bLeftAvail)
{
	int x, y, tmp;

	if (!bAboveAvail && !bLeftAvail){
		for (y = 0; y < iBlkHeight; y++){
			for (x = 0; x < iBlkWidth; x++){
				pDst[x] = 128;
			}
			pDst += i_dst;
		}
	}
	else if (bAboveAvail && !bLeftAvail){
		for (y = 0; y < iBlkHeight; y++){
			for (x = 0; x < iBlkWidth; x++){
				pDst[x] = (pel_t)((pSrc[x - 1] + 4 * pSrc[x] + 6 * pSrc[1 + x] + 4 * pSrc[2 + x] + pSrc[3 + x] + 8) >> 4);
			}
			pDst += i_dst;
		}
	}
	else if (!bAboveAvail && bLeftAvail){
		for (y = 0; y < iBlkHeight; y++){
			tmp = (pel_t)((pSrc[-y + 1] + 4 * pSrc[-y] + 6 * pSrc[-1 - y] + 4 * pSrc[-2 - y] + pSrc[-3 - y] + 8) >> 4);
			for (x = 0; x < iBlkWidth; x++){
				pDst[x] = tmp;
			}
			pDst += i_dst;
		}
	}
	else if (bAboveAvail && bLeftAvail){
		for (y = 0; y < iBlkHeight; y++){
			if(y == 0){
				tmp = ((pSrc[-y] + 4 * pSrc[-y] + 6 * pSrc[-1 - y] + 4 * pSrc[-2 - y] + pSrc[-3 - y] + 8) >> 4);
			}else{
				tmp = ((pSrc[-y + 1] + 4 * pSrc[-y] + 6 * pSrc[-1 - y] + 4 * pSrc[-2 - y] + pSrc[-3 - y] + 8) >> 4);
			}

			for (x = 0; x < iBlkWidth; x++){
				if(x == 0){
					pDst[x] = (pel_t)((((pSrc[x] + 4 * pSrc[x] + 6 * pSrc[1 + x] + 4 * pSrc[2 + x] + pSrc[3 + x] + 8) >> 4) + tmp) >> 1);
				}
				else{
					pDst[x] = (pel_t)((((pSrc[x - 1] + 4 * pSrc[x] + 6 * pSrc[1 + x] + 4 * pSrc[2 + x] + pSrc[3 + x] + 8) >> 4) + tmp) >> 1);
				}	
			}
			pDst += i_dst;
		}
	}

}


void xPredIntraVer(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight)
{
	int x, y;

	for (y = 0; y < iBlkHeight; y++){
		for (x = 0; x < iBlkWidth; x++){
			pDst[x] = pSrc[x + 1];
		}
		pDst += i_dst;
	}
}

void xPredIntraHor(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight)
{
	int x, y;

	for (y = 0; y < iBlkHeight; y++){
		for (x = 0; x < iBlkWidth; x++){
			pDst[x] = pSrc[-1 - y];
		}
		pDst += i_dst;
	}
}

void xPredIntraDownRight(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight)
{
	int x, y;

	for (y = 0; y < iBlkHeight; y++){
		for (x = 0; x < iBlkWidth; x++){
			pDst[x] = pSrc[x - y];
		}
		pDst += i_dst;
	}
}


void xPredIntraDownLeft(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight)
{
	int x, y;

	for (y = 0; y < iBlkHeight; y++){
		for (x = 0; x < iBlkWidth; x++){
			pDst[x] = (pSrc[2 + x + y] + pSrc[-2 - (x + y)]) >> 1;
		}
		pDst += i_dst;
	}

}

void xPredIntraChromaPlane(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight, int x_off, int y_off)
{
	int x, y, i;
	int ih, iv, ib, ic, iaa;
	ih = 0;
	iv = 0;

	for (i = 1; i < 5; i++)
	{
		ih += i*(pSrc[1 + i + 3] - pSrc[1 - i + 3]);
		iv += i*(pSrc[-1 - i - 3] - pSrc[-1 + i - 3]);
	}

	ib = (17 * ih + 16) >> 5;
	ic = (17 * iv + 16) >> 5;
	iaa = 16 * (pSrc[1 + 7] + pSrc[-1 - 7]);

	for (y = 0; y < iBlkHeight; y++){
		for (x = 0; x < iBlkWidth; x++){
			pDst[x] = (pel_t)max(0, min(255, (iaa + (x + x_off - 3)*ib + (y + y_off - 3)*ic + 16) / 32));
		}
		pDst += i_dst;
	}
}


void xPredIntraChromaDC(unsigned char *pSrc, pel_t *pDst, int i_dst, int iBlkWidth, int iBlkHeight, int x_off, int y_off, bool bAboveAvail, bool bLeftAvail)
{
	int x, y;

	if (!bAboveAvail && !bLeftAvail){
		for (y = 0; y < iBlkHeight; y++){
			for (x = 0; x < iBlkWidth; x++){
				pDst[x] = 128;
			}
			pDst += i_dst;
		}
	}
	else if (bAboveAvail && !bLeftAvail){
		pSrc = pSrc + x_off;
		for (y = 0; y < iBlkHeight; y++){
			for (x = 0; x < iBlkWidth; x++){
				pDst[x] = pSrc[1 + x];
			}
			pDst += i_dst;
		}
	}
	else if (!bAboveAvail && bLeftAvail){
		pSrc = pSrc - y_off;
		for (y = 0; y < iBlkHeight; y++){
			for (x = 0; x < iBlkWidth; x++){
				pDst[x] = pSrc[-1 - y];
			}
			pDst += i_dst;
		}
	}
	else if (bAboveAvail && bLeftAvail){
		for (y = 0; y < iBlkHeight; y++){
			for (x = 0; x < iBlkWidth; x++){
				pDst[x] = (pSrc[1 + x + x_off] + pSrc[-1 - y - y_off]) >> 1;
			}
			pDst += i_dst;
		}
	}
}

void intra_pred_luma(unsigned char *pSrc, unsigned char pDst[NO_INTRA_PMODE][256], int uhBlkWidth, bool block_available_up, bool block_available_left)
{
	int bs_y, bs_x;

	bs_x = bs_y = uhBlkWidth;

	// the img->mprr_flag need to be got individually
	get_mprrFlag(block_available_up, block_available_left);

	// 2 DC
	g_funs_handle.intra_pred_luma_dc(pSrc, pDst[DC_PRED], MB_BLOCK_SIZE, uhBlkWidth, uhBlkWidth, block_available_up, block_available_left);

	// 0 vertical
	if (block_available_up) {
		g_funs_handle.intra_pred_ver(pSrc, pDst[VERT_PRED], MB_BLOCK_SIZE, uhBlkWidth, uhBlkWidth);
	}

	// 1 horizontal
	if (block_available_left) {
		g_funs_handle.intra_pred_hor(pSrc, pDst[HOR_PRED], MB_BLOCK_SIZE, uhBlkWidth, uhBlkWidth);
	}

	// 4 down-right
	if (block_available_left&&block_available_up) {
		g_funs_handle.intra_pred_downright(pSrc, pDst[DOWN_RIGHT_PRED], MB_BLOCK_SIZE, uhBlkWidth, uhBlkWidth);
	}

	// 3 down-left
	if (block_available_left&&block_available_up) {
		g_funs_handle.intra_pred_downleft(pSrc, pDst[DOWN_LEFT_PRED], MB_BLOCK_SIZE, uhBlkWidth, uhBlkWidth);
	}
}

// function for chroma intrapred
void intra_pred_chroma(uchar_t *pSrc_DC, uchar_t *pSrc_NDC, int uv, int block_x, int block_y, bool mb_available_up, bool mb_available_left, bool mb_available_up_left)
{
	// DC prediction
	g_funs_handle.intra_pred_chroma_dc(pSrc_DC, img->mprr_c[uv][DC_PRED_8] + block_y * 16 + block_x, MB_BLOCK_SIZE, 4, 4, block_x, block_y, mb_available_up, mb_available_left);

	// vertical prediction
	if (mb_available_up)
	{
		g_funs_handle.intra_pred_ver(pSrc_NDC + block_x, img->mprr_c[uv][VERT_PRED_8] + block_y * 16 + block_x, MB_BLOCK_SIZE, 4, 4);
	}

	// horizontal prediction
	if (mb_available_left)
	{
		g_funs_handle.intra_pred_hor(pSrc_NDC - block_y, img->mprr_c[uv][HOR_PRED_8] + block_y * 16 + block_x, MB_BLOCK_SIZE, 4, 4);
	}

	// plane prediction
	if (mb_available_up_left)
	{
		g_funs_handle.intra_pred_chroma_plane(pSrc_NDC, img->mprr_c[uv][PLANE_8] + block_y * 16 + block_x, MB_BLOCK_SIZE, 4, 4, block_x, block_y);
	}

}

void com_funs_init_intra_pred()
{
	g_funs_handle.intra_pred_luma_dc = xPredIntraLumaDC;
	g_funs_handle.intra_pred_ver = xPredIntraVer;
	g_funs_handle.intra_pred_hor = xPredIntraHor;
	g_funs_handle.intra_pred_downright = xPredIntraDownRight;
	g_funs_handle.intra_pred_downleft = xPredIntraDownLeft;

	g_funs_handle.intra_pred_chroma_dc = xPredIntraChromaDC;
	g_funs_handle.intra_pred_chroma_plane = xPredIntraChromaPlane;
}
